package org.jheuristics.ga.multiObjective;

import org.jheuristics.Individual;
import org.jheuristics.ga.GAConfig;
import org.jheuristics.ga.GAStatus;
import org.jheuristics.ga.transformations.FitnessToDoubleTransformation;

/**
 * TODO
 *
 * @author Marcell Manfrin, marcell@ourgrid.org, Feb 2, 2006
 */
public class NormalizeFitness {
	private FitnessToDoubleTransformation[] transformations;

	/**
	 * TODO
	 * 
	 * @param transformations
	 */
	public NormalizeFitness(FitnessToDoubleTransformation[] transformations) {
		setTransformations(transformations);
	}

	/**
	 * TODO
	 * 
	 * @param transformations
	 */
	public void setTransformations(FitnessToDoubleTransformation[] transformations) {
		if (null == transformations) {
			// TODO: ERROR MSG
			throw new NullPointerException();
		}
		if (0 == transformations.length) {
			// TODO: ERROR MSG
			throw new IllegalArgumentException();
		}
		this.transformations = transformations;
	}

	/**
	 * TODO
	 * 
	 * @return
	 */
	public FitnessToDoubleTransformation[] getTransformations() {
		return transformations;
	}

	/**
	 * TODO
	 * 
	 * @param individuals
	 * @param status
	 * @param config
	 * @return
	 * @see org.jheuristics.ga.multiObjective.DistanceAlgorithm#getDistances(org.jheuristics.Individual[], org.jheuristics.ga.GAStatus,
	 * org.jheuristics.ga.GAConfig)
	 */
	public double[][] getNormalizedFitness(Individual[] individuals, GAStatus status, GAConfig config) {
		if (null == individuals) {
			throw new NullPointerException();
		}

		if (1 >= individuals.length) {
			throw new IllegalArgumentException();
		}

		for (int i = 0; i < individuals.length; i++) {
			Individual individual = individuals[i];
			if (null == individual)
				throw new NullPointerException();
			if (!(individual.getFitness() instanceof MultiObjectiveFitness)) {
				// TODO: ERROR MSG
				throw new ClassCastException();
			}
			MultiObjectiveFitness fitness = (MultiObjectiveFitness) individual.getFitness();
			if (transformations.length != fitness.getObjectives().length) {
				// TODO: ERROR MSG
				throw new IllegalArgumentException("transformations.length = " + transformations.length + " != objectives.length = " + fitness.getObjectives().length);
			}

		}

		return getNormalized(individuals);
	}

	/**
	 * TODO
	 *
	 * @param individuals
	 * @return
	 */
	private double[][] getNormalized(Individual[] individuals) {
		double[][] values = new double[individuals.length][transformations.length];
		for (int i = 0; i < individuals.length; i++) {
			Comparable[] fitnesses = ((MultiObjectiveFitness) individuals[i].getFitness()).getObjectives();
			for (int j = 0; j < transformations.length; j++) {
				values[i][j] = transformations[j].toDouble(fitnesses[j]);
			}
		}

		double[] min = new double[values[0].length];
		double[] max = new double[values[0].length];
		for (int j = 0; j < values[0].length; j++) {
			min[j] = values[0][j];
			max[j] = values[0][j];
		}
		for (int i = 1; i < values.length; i++) {
			for (int j = 0; j < values[i].length; j++) {
				if (min[j] > values[i][j]) {
					min[j] = values[i][j];
				}
				if (max[j] < values[i][j]) {
					max[j] = values[i][j];
				}
			}
		}
		for (int i = 0 ; i < values.length; i++) {
			for (int j = 0; j < values[i].length; j++) {
				double delta = max[j] - min[j];
				values[i][j] = (values[i][j] - min[j]) / (delta == 0.0 ? 1.0 : delta);
			}
		}
		return values;
	}

}
